package com.gy.game;

import com.gy.componit.*;
import com.gy.componit.Component;
import com.gy.event.GameEvent;
import com.gy.utils.CrashUtil;

import javax.swing.*;
import java.awt.*;
import java.math.BigDecimal;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

public class Game {

    private int frameHeight = 800, frameWidth = 1100;
    private JFrame jFrame;
    private MyPanel myPanel;
    // 地图
    private WarMap warMap;
    // 所有组件
    private Set<Component> allComp = new HashSet<>();
    // 组件按区块分组
    private Map<Area, List<Component>> areaCompMap = new HashMap<>();
    // 特殊组件
    private List<Component> specialComp = new ArrayList<>();
    // 区块
    private Map<AreaPoint, Area> areaMap = new HashMap<AreaPoint, Area>();
    // 队伍
    private List<Team> teams = new ArrayList<>();
    // 事件
    private List<GameEvent> events = new ArrayList<>();

    private final int areaSize = 30;

    public Game() {
        init();
    }

    void init() {
        createFrame();
        initWarMap();
    }

    private void initWarMap() {
        // 队伍
        teams.add(new Team(Color.red));
        teams.add(new Team(Color.YELLOW));
        teams.add(new Team(Color.GREEN));
        teams.add(new Team(Color.blue));
        // 区块
        for(int i = 0; (i+1) * areaSize < frameWidth; i++) {
            for(int j = 0; (j+1) * areaSize < frameHeight + 100; j++) {
                Area area = new Area();
                area.setX(i);
                area.setY(j);
                area.setHeight(areaSize);
                area.setWidth(areaSize);
                areaMap.put(new AreaPoint(i, j), area);
                areaCompMap.putIfAbsent(area, new LinkedList<>());
            }
        }
        // 地图
        this.warMap = new WarMap();
        warMap.setX(200);
        warMap.setY(50);
        warMap.setHeight(700);
        warMap.setWidth(700);
        specialComp.add(warMap);
        allComp.add(warMap);
        // 地块
        int plotSize = 10;
        int smallBorder = plotSize * 4;
        int bigBorder = warMap.getWidth() - plotSize * 4;
        List<List<Plot>> plotList = new ArrayList<>();
        warMap.setPlotSize(plotSize);
        int col = 0, row = 0;
        for(int x = warMap.getX(); x < warMap.getWidth() + warMap.getX(); x += plotSize) {
            List<Plot> plotRow = new ArrayList<>();
            for(int y = warMap.getY(); y < warMap.getHeight() + warMap.getY(); y += plotSize) {
                Plot plot = new Plot();
                plot.setX(x);
                plot.setY(y);
                plot.setSize(plotSize);
                plot.setRow(row);
                plot.setCol(col++);
                plotRow.add(plot);
                // 初始地块颜色
                if(x-warMap.getX() < smallBorder && y-warMap.getY() < smallBorder) {
                    plot.setTeam(teams.get(0));
                }else if(x-warMap.getX() >= bigBorder && y-warMap.getY() < smallBorder) {
                    plot.setTeam(teams.get(1));
                }else if(x-warMap.getX() >= bigBorder && y-warMap.getY() >= bigBorder) {
                    plot.setTeam(teams.get(2));
                }else if(x-warMap.getX() < smallBorder && y-warMap.getY() >= bigBorder) {
                    plot.setTeam(teams.get(3));
                }
            }
            plotList.add(plotRow);
            row++;
            col = 0;
        }
        warMap.initPlotList(plotList);
        // 炮塔
        int dis = 10; // 距边
        Battery battery1 = new Battery();
        battery1.setStartAngle(-45);
        battery1.setX(new BigDecimal(warMap.getX() + dis));
        battery1.setY(new BigDecimal(warMap.getY() + dis));
        battery1.setTeam(teams.get(0));
        allComp.add(battery1);
        areaCompMap.get(areaMap.get(battery1.getAreaPoint(areaSize))).add(battery1);

        Battery battery2 = new Battery();
        battery2.setStartAngle(-135);
        battery2.setX(new BigDecimal(warMap.getX() + 700-dis));
        battery2.setY(new BigDecimal(warMap.getY() + dis));
        battery2.setTeam(teams.get(1));
        allComp.add(battery2);
        areaCompMap.get(areaMap.get(battery2.getAreaPoint(areaSize))).add(battery2);

        Battery battery3 = new Battery();
        battery3.setStartAngle(135);
        battery3.setX(new BigDecimal(warMap.getX() + 700-dis));
        battery3.setY(new BigDecimal(warMap.getY() + 700-dis));
        battery3.setTeam(teams.get(2));
        allComp.add(battery3);
        areaCompMap.get(areaMap.get(battery3.getAreaPoint(areaSize))).add(battery3);

        Battery battery4 = new Battery();
        battery4.setStartAngle(45);
        battery4.setX(new BigDecimal(warMap.getX() + dis));
        battery4.setY(new BigDecimal(warMap.getY() + 700-dis));
        battery4.setTeam(teams.get(3));
        allComp.add(battery4);
        areaCompMap.get(areaMap.get(battery4.getAreaPoint(areaSize))).add(battery4);

    }

    private void move() {
        for(Component comp: allComp) {
            AreaPoint pointBefore = comp.getAreaPoint(areaSize);
            comp.move(this);
            AreaPoint pointAfter = comp.getAreaPoint(areaSize);
            if(pointAfter != null && pointBefore != null && !pointBefore.equals(pointAfter)) {
                Area area = areaMap.get(pointBefore);
                List<Component> components = areaCompMap.get(area);
                components.remove(comp);
                Area area1 = areaMap.get(pointAfter);
                List<Component> components1 = areaCompMap.get(area1);
                if(components1 != null) {
                    components1.add(comp);
                }
            }
        }
    }

    public void start() {
        //重复绘制
        int count = 0;
        int fps = 60;
        while (true){
            // 移动
            move();
            // 事件
            handleEvent();
            if(count >= 1000/(50*fps)) {
                this.myPanel.setAllComp(allComp);
                this.jFrame.repaint();
                count = 0;
            }
            count ++;
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void handleEvent() {
        for(GameEvent event: events) {
            event.invoke(this);
        }
        events.clear();
    }

    private void createFrame() {
        JFrame frame = new JFrame();
        MyPanel panel = new MyPanel();
        this.jFrame = frame;
        this.myPanel = panel;
        frame.add(panel);
        frame.getContentPane().setBackground(Color.getHSBColor(233, 233, 233));
        //设置窗口是否可见
        frame.setVisible(true);
        //设置窗口大小
        frame.setSize(frameWidth,frameHeight);
        //设置窗口位置
        frame.setLocationRelativeTo(null);
        //设置窗口标题
        frame.setTitle("球球战争");
    }

    public void addEvent(GameEvent event) {
        this.events.add(event);
    }

    public void registerComponent(Component component, boolean special) {
        synchronized (allComp) {
            allComp.add(component);
        }
        if(special) {
            specialComp.add(component);
        }else {
            AreaPoint areaPoint = component.getAreaPoint(areaSize);
            List<Component> components = areaCompMap.get(areaMap.get(areaPoint));
            components.add(component);
        }
    }

    public void removeComponent(Component component, boolean special) {
        synchronized (allComp) {
            allComp.remove(component);
        }
        if(special) {
            specialComp.remove(component);
        }else {
            Area area = areaMap.get(component.getAreaPoint(areaSize));
            List<Component> components = areaCompMap.get(area);
            components.remove(component);
        }
    }

    // 获取周围的区块
    public List<Area> getAboutAreas(AreaPoint areaPoint) {
        int x = areaPoint.getX();
        int y = areaPoint.getY();
        Set<Area> set = new HashSet<>();
        AreaPoint point = new AreaPoint(x, y);
        set.add(areaMap.get(point.reset(x, y)));
        set.add(areaMap.get(point.reset(x-1, y-1)));
        set.add(areaMap.get(point.reset(x-1, y)));
        set.add(areaMap.get(point.reset(x-1, y+1)));
        set.add(areaMap.get(point.reset(x, y-1)));
        set.add(areaMap.get(point.reset(x, y+1)));
        set.add(areaMap.get(point.reset(x+1, y-1)));
        set.add(areaMap.get(point.reset(x+1, y)));
        set.add(areaMap.get(point.reset(x+1, y+1)));
        List<Area> list = set.stream().collect(Collectors.toList());
        return list;
    }

    // 获取区块里所有的元素
    public Collection<Component> getComponents(Area ... areas) {
        Set<Component> set = new HashSet<>();
        for(Area area: areas) {
            List<Component> components = areaCompMap.get(area);
            if(components == null) continue;
            set.addAll(components);
        }
        return set;
    }

    public int getAreaSize() {
        return areaSize;
    }

    public WarMap getWarMap() {
        return warMap;
    }
}
